home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995 August: Tool Chest / Dev.CD Aug 95 TC / Dev.CD Aug 95 TC.toast / Tool Chest / Interapplication Communication / Server Remote Control 1.1 / Remote Control.p < prev    next >
Encoding:
Text File  |  1995-06-20  |  53.9 KB  |  1,914 lines  |  [TEXT/MWPS]

  1. {$N-}
  2.  
  3. { Remote Control code }
  4. { © Copyright 1991-1995 Jim Luther, All rights reserved. }
  5.  
  6. { Modification History: }
  7. { 28 Dec 91    JML        V1.0d1    First working version with System 7 File Sharing }
  8. { 31 Dec 91    JML        V1.0d1    Added check for Gestalt trap to InitializeApp. }
  9. { 31 Dec 91    JML        V1.0d1    Changed RemoteSCPBRec structure (added scSetup field) }
  10. { 31 Dec 91    JML        V1.0d1    Hacked up every routine that uses SCPSJustDisabled to }
  11. {                            also use SCPSSleeping. }
  12. { 31 Dec 91    JML        V1.0d2    Works with AppleShare 3.0 }
  13. { 31 Dec 91    JML        V1.0d2    Added "Already controlled" alert if StartSecureSession returns }
  14. {                            noInformErr }
  15. {  2 Jan 92    JML        V1.0d2    Got my creator assignments, so I added a BNDL, ICN#, etc and }
  16. {                            changed a few things to use the new creator types and the }
  17. {                            version resources. }
  18. {  3 Jan 92    JML        V1.0d2+    Restored the Initialization OFF directive for THINK Pascal }
  19. {                            that somehow was deleted... }
  20. {  3 Jan 92    JML        V1.0d2++ Handle default user changed on remote system at }
  21. {                            StartSecureSession. }
  22. { 10 Mar 93    JML        V1.0    Ahhh, just release the dang thing. It works fine. }
  23. { 21 May 95    JML        V1.1b1    Make it compile with Metrowerks Pascal CW6 and }
  24. {                            Universal Interfaces (what a hassle!). }
  25. { 22 May 95    JML        V1.1b1    Added Open and Save connection document code. }
  26. { 22 May 95    JML        V1.1b1    Added open document AppleEvent handler code. }
  27. { 22 May 95    JML        V1.1b1    Cached "NoLocation" name - the GetMyZone call for every update }
  28. {                            was killing update performance. }
  29.  
  30. {$IFC UNDEFINED THINK_Pascal}
  31. {$ELSEC}
  32. {$I-}
  33. {$ENDC}
  34.  
  35. PROGRAM RemoteShare;
  36.  
  37.     USES
  38.         Types, Memory, Packages, Errors, Quickdraw, Controls, Fonts, Dialogs, Windows, Menus, Events, DiskInit, OSUtils, Resources, ToolUtils, AppleTalk, Processes, PPCToolbox, EPPC, Notification, AppleEvents, GestaltEqu, Traps, Balloons, Script, TextEdit, Devices, SegLoad, StandardFile, ServerControlIntf;
  39.  
  40.     CONST
  41.         kPortName = 'Remote Control';        { should use a resource string }
  42.  
  43.         kControllerCreator = 'ASsc';    { used for remote PPC port's portCreator and nbpType }
  44.         kRemoteCreator = 'ASrc';        { used for our PPC port's portCreator }
  45.         kRemoteConnectionType = 'SRCC';    { used for saved connection files }
  46.  
  47.     { Saved connection file resource ID and types }
  48.         kSavedConnectionID = 128;
  49.         kSavedPortInfoRec = 'SPIR';
  50.         kSavedSessLocationName = 'SSLN';
  51.         kSaveConnectionPrompt = 128;    { 'STR ' for StandardPutFile prompt }
  52.  
  53.     { window, dialog, and alert resources }
  54.         kAboutBox = 200;        { AboutBox alert }
  55.  
  56.         kMyWindowResource = 500;    { the window and window title STR resource IDs }
  57.         kRemoteMac = 501;            { 'Remote Macintosh' RECT and STR resource IDs }
  58.         kRemoteFileSrvr = 502;        { 'Remote File Server' RECT and STR resource IDs }
  59.         kRemoteMacNameLabel = 503;    { 'Macintosh Name:' RECT and STR resource IDs }
  60.         kRemoteMacName = 504;        { Macintosh name RECT resource ID }
  61.         kRemoteZoneLabel = 505;        { 'Zone:' RECT and STR resource IDs }
  62.         kRemoteZoneName = 506;        { Zone name RECT resource ID }
  63.         kRemoteStatusLabel = 507;    { 'Status:' RECT and STR resource IDs }
  64.  
  65.         kRemoteStatusText = 508;    { Status text RECT and STR# resource IDs }
  66.         kStatusOff = 1;
  67.         kStatusStarting = 2;
  68.         kStatusOn = 3;
  69.         kStatusLessThan1Minute = 4;
  70.         kStatusOneMinute = 5;
  71.         kStatusXMinutes = 6;
  72.         kStatusSleeping = 7;
  73.  
  74.         kRemoteDivLine = 509;        { Dividing line RECT resource IDs }
  75.  
  76.         kWindowButton = 510;        { the button control and STR# resource IDs }
  77.         kButtonStart = 1;
  78.         kButtonCancel = 2;
  79.         kButtonStop = 3;
  80.  
  81.         kNotOwnerAlert = 501;    { remote is not owner alert }
  82.  
  83.         kShutdownDialog = 502;    { how long before shutdown dialog }
  84.         kShutdownTime = 3;        { editText item }
  85.         kShutdownOKOutline = 5;    { user item number of OK outline }
  86.  
  87.         kAlreadyControlled = 503;    { the server is already controlled alert }
  88.  
  89.         kSampHelp = 600;    { Help dialog }
  90.  
  91.  
  92.     { menu constants }
  93.         kMBarID = 128;
  94.  
  95.         kAppleMenu = 128;    { the Apple menu }
  96.  
  97.         kFileMenu = 129;    { the File menu }
  98.         kOpenItem = 1;
  99.         kSaveItem = 2;
  100.         kQuitItem = 4;
  101.  
  102.         kEditMenu = 130;    { the Edit menu }
  103.  
  104.         kRemoteMenu = 131;    { the Sharing menu }
  105.         kConnectToRemote = 1;    { the connect/disconnect menu item }
  106.         kConnectString = 1;        { 'STR#' item numbers }
  107.         kDisconnectString = 2;
  108.  
  109.         kHelpString = 600;    { help menu item string }
  110.  
  111.  
  112.     { PPCReject rejectInfo codes }
  113.         kRemoteIsNotOwner = 1;
  114.         kRemoteAppUnknown = -1;    { •••Remote Control should never get this }
  115.  
  116.         kPPCIOBufSize = 2048;    { kPPCIOBufSize is the size of the I/O buffer used by }
  117.                                 { the session.  2K is big enough to send or receive   }
  118.                                 { anything a server control call will use. }
  119.  
  120.         kExitErrorStrings = 128;
  121.         kNumExitErrors = 10;    { number of exit errors }
  122.         { ExitToShell errors }
  123.         kExitNoSystem7 = 1;
  124.         kExitNoAppleEvts = 2;
  125.         kExitAEHandlerNotInstalled = 3;
  126.         kExitNoOwnerName = 4;    { •••not used in Remote Control }
  127.         kExitNoPPC = 5;
  128.         kExitPPCInitFailed = 6;
  129.         kExitAppleTalkDisabled = 7;
  130.         kExitProgramLinkingDisabled = 8;    { •••not used in Remote Control }
  131.         kExitPPCOpenFailed = 9;
  132.         kExitPPCInformFailed = 10;    { •••not used in Remote Control }
  133.  
  134.         {ServerControl constants}
  135.         kSIMaxLogins = 200;
  136.  
  137.         kVerNumType = $01008000;        { NumVersion.version used for the remote and our }
  138.                                     { PPC ports' portType. In this case version 1.0.0 final. }
  139.  
  140.     TYPE
  141.     { Handle types for saving connection }
  142.         PortInfoHandle = ^PortInfoPtr;
  143.         LocationNameHandle = ^LocationNamePtr;
  144.  
  145.         RemoteSCPBRecPtr = ^RemoteSCPBRec;
  146.         RemoteSCPBRec = RECORD
  147.                 scPB: SCParamBlockRec;
  148.                 scMessageOrName: Str255;
  149.                 scDiscArray: ARRAY[1..kSIMaxLogins] OF LongInt;
  150.                 scSetup: SetupInfoRec;
  151.             END;
  152.         PPCIOBuffer = RemoteSCPBRec;
  153.  
  154.         RectHndl = ^RectPtr;
  155.  
  156.     VAR
  157.         gQuit, gInBackground: Boolean;
  158.  
  159.         gMymenu: Handle;    { my menu bar handle }
  160.         gAppleMenuHandle, gFileMenuHandle, gEditMenuHandle, gRemoteMenuHandle: MenuHandle;
  161.  
  162.         myWindow: WindowPtr;
  163.         gWindowButton: ControlRef;
  164.         gWindBttnHilite: Integer;
  165.         gHelpItem: Integer;    { 0 if no help item }
  166.  
  167.         gOurPSN: ProcessSerialNumber;
  168.         gShortVersStr: Str255;            { NumVersion.shortVersion used for about box }
  169.  
  170.         gPPCPortOpen: Boolean;            { TRUE when the PPC port has been opened }
  171.         gPPCPortRefNum: PPCPortRefNum;    { The PPC port reference number }
  172.         gPPCSessRefNum: PPCSessRefNum;  { The PPC session reference number }
  173.  
  174.         gPPCGeneralRec: PPCParamBlockRec;
  175.         gPPCSessPortName: PPCPortRec;
  176.         gPPCSessLocationName: LocationNameRec;
  177.         gPPCSessUserName: Str32;
  178.         gPPCReadBuffer: PPCIOBuffer;
  179.         gPPCDataRead: Boolean;
  180.         gPortInfoRec: PortInfoRec;
  181.  
  182.         gRemoteSCpb: RemoteSCPBRec;
  183.         gFirstStatus: Boolean;
  184.         oldSCServerState: Integer;
  185.  
  186.         gPPCWriteRec: PPCParamBlockRec;    { used for PPCWrite calls }
  187.         gPPCWriteInProgress: Boolean;
  188.  
  189.  
  190.         gPPCEndRec: PPCParamBlockRec;    { used for PPCEnd calls }
  191.  
  192.         gNotificationMgrPresent: Boolean;
  193.         gNMRec: NMRec;
  194.         gNMStrs: ARRAY[1..kNumExitErrors] OF Str255;
  195.  
  196.         gNoLocationZoneName: Str32;
  197.  
  198. {==============================================================================}
  199.  
  200. {$S Main}
  201.     PROCEDURE NotifyResponseProc (nmReqPtr: NMRecPtr);
  202.         VAR
  203.             oldA5: LongInt;
  204.             err: OSErr;
  205.     BEGIN
  206.         oldA5 := SetA5(nmReqPtr^.nmRefCon);
  207.         gQuit := TRUE;
  208.         err := WakeUpProcess(gOurPSN);
  209.         err := NMRemove(nmReqPtr);
  210.         oldA5 := SetA5(oldA5);
  211.     END;
  212.  
  213. {------------------------------------------------------------------------------}
  214.  
  215. {$S Main}
  216.     PROCEDURE NotifyAndExit (errCode: Integer);
  217.     BEGIN
  218.         IF gNotificationMgrPresent THEN
  219.             BEGIN
  220.                 WITH gNMRec DO
  221.                     BEGIN
  222.                         qType := ORD(nmType);
  223.                         nmMark := 0;
  224.                         nmIcon := NIL;
  225.                         nmSound := Handle(-1);
  226.                         nmStr := @gNMStrs[errCode];
  227.                         nmResp := @NotifyResponseProc;
  228.                         nmRefCon := SetCurrentA5;
  229.                     END;
  230.  
  231.                 IF (NMInstall(@gNMRec) <> noErr) THEN
  232.                     BEGIN
  233.                         gQuit := TRUE;
  234.                     END;
  235.             END
  236.         ELSE
  237.             BEGIN
  238.                 gQuit := TRUE;
  239.             END;
  240.     END;
  241.  
  242. {------------------------------------------------------------------------------}
  243.  
  244. {$S Main}
  245. { DoDiskEvents just checks the error code from the disk mount, }
  246. { and puts up the 'Format' dialog (through DIBadMount) if need be }
  247. { You can do much more here if you care about what disks are }
  248. { in the drive }
  249.     PROCEDURE DoDiskEvents (dinfo: LongInt);
  250. { hi word is error code, lo word is drive number }
  251.         VAR
  252.             hival, loval, tommy: Integer;
  253.             fredpoint: Point;
  254.     BEGIN
  255.         fredpoint.v := 40;
  256.         fredpoint.h := 40;
  257.         hival := HiWord(dinfo);
  258.         loval := LoWord(dinfo);
  259.         IF hival <> noErr THEN    { something happened }
  260.             BEGIN
  261.                 tommy := DIBadMount(fredpoint, dinfo);
  262.             END;
  263.     END;
  264.  
  265. {------------------------------------------------------------------------------}
  266.  
  267. {$S Main}
  268. { This is my sample help dialog. It doesn't do anything. Expand as you need. }
  269.     PROCEDURE SampleHelpDialog;
  270.         VAR
  271.             tdial: DialogPtr;
  272.             itemHit: Integer;
  273.     BEGIN
  274.         tdial := GetNewDialog(kSampHelp, NIL, WindowPtr(-1));
  275.         REPEAT
  276.             ModalDialog(NIL, itemhit);
  277.         UNTIL (itemhit = 1);
  278.         DisposeDialog(tdial);
  279.     END;
  280.  
  281. {------------------------------------------------------------------------------}
  282.  
  283. {$S Main}
  284.     PROCEDURE HiliteMyButton;
  285.         CONST
  286.             active = 0;
  287.             inactive = 255;
  288.     BEGIN
  289.         IF (gWindowButton <> NIL) THEN
  290.             BEGIN
  291.                 IF ((NOT gPPCWriteInProgress) AND (NOT gInBackground) AND (NOT gFirstStatus)) THEN
  292.                     BEGIN
  293.                         IF (gWindBttnHilite <> active) THEN
  294.                             BEGIN
  295.                                 HiliteControl(gWindowButton, active);
  296.                                 gWindBttnHilite := active;
  297.                             END;
  298.                     END
  299.                 ELSE
  300.                     BEGIN
  301.                         IF (gWindBttnHilite <> inactive) THEN
  302.                             BEGIN
  303.                                 HiliteControl(gWindowButton, inactive);
  304.                                 gWindBttnHilite := inactive;
  305.                             END;
  306.                     END;
  307.             END;
  308.     END;
  309.  
  310. {------------------------------------------------------------------------------}
  311.  
  312. {$S Main}
  313. { draws my window. }
  314.     PROCEDURE DrawMain (drawit: WindowPtr);
  315.         VAR
  316.             r: Rect;
  317.             h: Handle;
  318.             strH: StringHandle;
  319.             str, numStr: Str255;
  320.             i: Integer;
  321.             theXPPPB: XPPParamBlock;
  322.     BEGIN
  323.         BeginUpdate(drawIt);
  324.         SetPort(drawIt);
  325.  
  326.         DrawControls(drawIt);
  327.  
  328.         TextFont(systemFont);
  329.         TextSize(12);
  330.         TextFace([]);
  331.  
  332.         h := GetResource('RECT', kRemoteMac);
  333.         r := RectHndl(h)^^;
  334.         MoveTo(r.left, r.bottom);
  335.         strH := GetString(kRemoteMac);
  336.         str := strH^^;
  337.         DrawString(str);
  338.  
  339.         h := GetResource('RECT', kRemoteFileSrvr);
  340.         r := RectHndl(h)^^;
  341.         MoveTo(r.left, r.bottom);
  342.         strH := GetString(kRemoteFileSrvr);
  343.         str := strH^^;
  344.         DrawString(str);
  345.  
  346.         TextFont(geneva);
  347.         TextSize(9);
  348.  
  349.         TextFace([bold]);
  350.  
  351.         h := GetResource('RECT', kRemoteMacNameLabel);
  352.         r := RectHndl(h)^^;
  353.         MoveTo(r.left, r.bottom);
  354.         strH := GetString(kRemoteMacNameLabel);
  355.         str := strH^^;
  356.         DrawString(str);
  357.  
  358.         h := GetResource('RECT', kRemoteZoneLabel);
  359.         r := RectHndl(h)^^;
  360.         MoveTo(r.left, r.bottom);
  361.         strH := GetString(kRemoteZoneLabel);
  362.         str := strH^^;
  363.         DrawString(str);
  364.  
  365.         h := GetResource('RECT', kRemoteStatusLabel);
  366.         r := RectHndl(h)^^;
  367.         MoveTo(r.left, r.bottom);
  368.         strH := GetString(kRemoteStatusLabel);
  369.         str := strH^^;
  370.         DrawString(str);
  371.  
  372.         TextFace([]);
  373.  
  374.         h := GetResource('RECT', kRemoteMacName);
  375.         r := RectHndl(h)^^;
  376.         MoveTo(r.left, r.bottom);
  377.         IF (gPPCSessLocationName.locationKindSelector = ppcNoLocation) THEN
  378.             BEGIN
  379.                 strH := GetString(-16413);
  380.                 str := strH^^;
  381.                 DrawString(str);
  382.             END
  383.         ELSE
  384.             BEGIN
  385.                 DrawString(gPPCSessLocationName.nbpEntity.objStr);
  386.             END;
  387.  
  388.         h := GetResource('RECT', kRemoteZoneName);
  389.         r := RectHndl(h)^^;
  390.         MoveTo(r.left, r.bottom);
  391.         IF (gPPCSessLocationName.locationKindSelector = ppcNoLocation) THEN
  392.             BEGIN
  393.                 IF (gNoLocationZoneName = '') THEN
  394.                     BEGIN
  395.                         WITH theXPPPB DO
  396.                             BEGIN
  397.                                 xppTimeOut := 1;
  398.                                 xppRetry := 3;
  399.                                 zipBuffPtr := @gNoLocationZoneName;
  400.                                 zipInfoField[1] := 0;
  401.                                 zipInfoField[2] := 0;
  402.                             END;
  403.                         IF GetMyZone(@theXPPPB, FALSE) <> noErr THEN
  404.                             BEGIN
  405.                                 gNoLocationZoneName := '*';
  406.                             END;
  407.                     END;
  408.                 DrawString(gNoLocationZoneName);
  409.             END
  410.         ELSE
  411.             BEGIN
  412.                 DrawString(gPPCSessLocationName.nbpEntity.zoneStr);
  413.             END;
  414.  
  415.         IF (NOT gFirstStatus) THEN
  416.             BEGIN
  417.                 h := GetResource('RECT', kRemoteStatusText);
  418.                 r := RectHndl(h)^^;
  419.                 CASE oldSCServerState OF
  420.                     SCPSSleeping: 
  421.                         BEGIN
  422.                             GetIndString(str, kRemoteStatusText, kStatusSleeping);
  423.                         END;
  424.                     SCPSJustDisabled: 
  425.                         BEGIN
  426.                             GetIndString(str, kRemoteStatusText, kStatusOff);
  427.                         END;
  428.                     SCPSStartingUp: 
  429.                         BEGIN
  430.                             GetIndString(str, kRemoteStatusText, kStatusStarting);
  431.                         END;
  432.                     SCPSRunning: 
  433.                         BEGIN
  434.                             GetIndString(str, kRemoteStatusText, kStatusOn);
  435.                         END;
  436.                     0: 
  437.                         BEGIN
  438.                             GetIndString(str, kRemoteStatusText, kStatusLessThan1Minute);
  439.                         END;
  440.                     1: 
  441.                         BEGIN
  442.                             GetIndString(str, kRemoteStatusText, kStatusOneMinute);
  443.                         END;
  444.                     OTHERWISE
  445.                         BEGIN
  446.                             GetIndString(str, kRemoteStatusText, kStatusXMinutes);
  447.                             i := Pos('^', str);    { find marker }
  448.                             Delete(str, i, 1);    { delete marker }
  449.                             NumToString(oldSCServerState, numStr);
  450.                             Insert(numStr, str, i);    { insert numStr }
  451.                         END;
  452.                 END;
  453.                 TETextBox(@str[1], Length(str), r, teJustLeft);
  454.             END;
  455.  
  456.         PenNormal;
  457.  
  458.         h := GetResource('RECT', kRemoteDivLine);
  459.         r := RectHndl(h)^^;
  460.         MoveTo(r.left, r.bottom);
  461.         LineTo(r.right, r.bottom);
  462.  
  463.         EndUpdate(drawIt);
  464.     END;
  465.  
  466. {------------------------------------------------------------------------------}
  467.  
  468. {$S Main}
  469.     PROCEDURE EndCompProc (pb: PPCParamBlockPtr);
  470.     { This procedure gets called when the asynchronous PPCEnd call completes. }
  471.  
  472.     BEGIN
  473.         gFirstStatus := TRUE;    { reset gFirstStatus so first status returned by a }
  474.                                 { new connection updates the status message }
  475.         gPPCSessRefNum := 0; { the session is closing }
  476.     END;
  477.  
  478. {------------------------------------------------------------------------------}
  479.  
  480. {$S Main}
  481.     PROCEDURE ReadCompProc (pb: PPCParamBlockPtr);
  482.     { This procedure gets called when the asynchronous PPCRead call completes.}
  483.     { If no errors are detected, then it puts the parameter block in the }
  484.     { gRpbQueue where the PPCProcessReads procedure will find it and process }
  485.     { the data read.  PPCProcessReads will make another PPCRead call. }
  486.     { If an error is detected, then PPCEnd is called asynchronously to close}
  487.     { the session. }
  488.  
  489.         VAR
  490.             err: OSErr;    { used to catch the PPC function results. }
  491.  
  492.     BEGIN
  493.         IF (PPCReadPBPtr(pb)^.ioResult = noErr) THEN
  494.             BEGIN
  495.                 gPPCDataRead := TRUE;
  496.                 err := WakeUpProcess(gOurPSN);
  497.             END
  498.         ELSE
  499.             BEGIN
  500.                 { if we get an error, then we call PPCEnd to close up cleanly}
  501.                 WITH pb^.endParam DO
  502.                     BEGIN
  503.                         ioCompletion := @EndCompProc;
  504.                         sessRefNum := gPPCSessRefNum;
  505.                     END;
  506.                 err := PPCEnd(@pb^.endParam, TRUE);
  507.             END;
  508.     END;
  509.  
  510. {------------------------------------------------------------------------------}
  511.  
  512. {$S Main}
  513.     FUNCTION myBrowserPortFilter (theLocationNameRec: LocationNameRec;
  514.                                     thePortInfoRec: PortInfoRec): Boolean;
  515.  
  516.     BEGIN
  517.         { filter on creator type and majorRev field of version number }
  518.         IF (thePortInfoRec.name.portKindSelector = ppcByCreatorAndType) THEN
  519.             BEGIN
  520.                 IF ((thePortInfoRec.name.portCreator = kControllerCreator) AND (LongInt(thePortInfoRec.name.portType) = kVerNumType)) THEN
  521.                     BEGIN
  522.                         myBrowserPortFilter := TRUE;
  523.                     END
  524.                 ELSE
  525.                     BEGIN
  526.                         myBrowserPortFilter := FALSE;
  527.                     END;
  528.             END
  529.         ELSE
  530.             BEGIN
  531.                 myBrowserPortFilter := FALSE;
  532.             END;
  533.     END;
  534.  
  535. {------------------------------------------------------------------------------}
  536.  
  537. {$S Main}
  538.     FUNCTION myPPCBrowser (VAR theLocationNameRec: LocationNameRec;
  539.                                     VAR thePortInfoRec: PortInfoRec): OSErr;
  540.  
  541.         VAR
  542.             theLocNBPType: Str32;
  543.  
  544.     BEGIN
  545.         theLocNBPType := kControllerCreator;    { Match this NBP type }
  546.  
  547.         myPPCBrowser := PPCBrowser('', '', FALSE, theLocationNameRec, thePortInfoRec, @myBrowserPortFilter, theLocNBPType);
  548.     END;
  549.  
  550. {------------------------------------------------------------------------------}
  551.  
  552. {$S Main}
  553.     FUNCTION DeleteNewUserRefNum (newUserRef: LongInt): OSErr;
  554.  
  555.         VAR
  556.             err: OSErr;
  557.             defUserRef: LongInt;
  558.             defUserName: Str32;
  559.  
  560.     BEGIN
  561.         IF (newUserRef <> 0) THEN
  562.             BEGIN
  563.                 err := GetDefaultUser(defUserRef, defUserName);
  564.                 IF (err = noErr) THEN    { there is a default user }
  565.                     BEGIN
  566.                         IF newUserRef <> defUserRef THEN    { it's not the default, so delete it }
  567.                             err := DeleteUserIdentity(newUserRef);
  568.                     END
  569.                 ELSE { there is no default, so delete it }
  570.                     BEGIN
  571.                         err := DeleteUserIdentity(newUserRef);
  572.                     END;
  573.                 DeleteNewUserRefNum := err;
  574.             END
  575.         ELSE { user reference number passed was the guest }
  576.             BEGIN
  577.                 deleteNewUserRefNum := noErr;
  578.             END;
  579.     END;
  580.  
  581. {------------------------------------------------------------------------------}
  582.  
  583. {$S Main}
  584.     FUNCTION myStartSecureSession (VAR theSessRefNum: PPCSessRefNum;
  585.                                     VAR theRejectInfo: LongInt): OSErr;
  586.  
  587.         VAR
  588.             thePPCStartPBRec: PPCStartPBRec;
  589.             useDefault: Boolean;
  590.             guestSelected: Boolean;
  591.             userName: Str32;
  592.             err: OSErr;
  593.  
  594.     BEGIN
  595.         WITH thePPCStartPBRec DO
  596.             BEGIN
  597.                 ioCompletion := NIL;
  598.                 portRefNum := gPPCPortRefNum;    { from PPCOpen }
  599.                 serviceType := CHAR(ppcServiceRealTime);
  600.                 resFlag := 0;
  601.                 portName := @gPortInfoRec.name; { from PPCBrowser }
  602.                 locationName := @gPPCSessLocationName; { from PPCBrowser }
  603.                 userData := 0; { not used }
  604.             END;
  605.  
  606.         { Try to connect with default user identity }
  607.         useDefault := TRUE;
  608.         userName := '';
  609.  
  610.         err := StartSecureSession(@thePPCStartPBRec, userName, useDefault, FALSE, guestSelected, stringPtr(NIL)^);
  611.         IF (err = noUserNameErr) THEN
  612.             BEGIN
  613.                 useDefault := FALSE;
  614.                 err := StartSecureSession(@thePPCStartPBRec, userName, useDefault, FALSE, guestSelected, stringPtr(NIL)^);
  615.             END;
  616.  
  617.         IF (err = noErr) THEN
  618.             BEGIN
  619.                 theSessRefNum := thePPCStartPBRec.sessRefNum;
  620.                 err := DeleteNewUserRefNum(thePPCStartPBRec.userRefNum);
  621.                 err := noErr; { I don't want to return error from DeleteNewUserRefNum }
  622.             END
  623.         ELSE IF err = userRejectErr THEN    { return the rejectInfo from PPCReject }
  624.             theRejectInfo := thePPCStartPBRec.rejectInfo;
  625.  
  626.         myStartSecureSession := err;
  627.     END;
  628.  
  629. {------------------------------------------------------------------------------}
  630.  
  631. {$S Main}
  632.     PROCEDURE StartConnection;
  633.         VAR
  634.             err: OSErr;
  635.             rejectInfo: LongInt;
  636.             qq: Integer;
  637.             WindowName: Handle;
  638.     BEGIN
  639.         err := myStartSecureSession(gPPCSessRefNum, rejectInfo);
  640.         IF (err <> noErr) THEN
  641.             BEGIN
  642.                 CASE err OF
  643.                     userRejectErr: 
  644.                         BEGIN
  645.                             CASE rejectInfo OF {probably won't need this CASE, but if I think of any other reject errors...}
  646.                                 kRemoteIsNotOwner: 
  647.                                     qq := Alert(kNotOwnerAlert, NIL); { put up alert }
  648.                             END;
  649.                             gPPCSessRefNum := 0;
  650.                             Exit(StartConnection);
  651.                         END;
  652.                     noInformErr: 
  653.                         BEGIN
  654.                             qq := Alert(kAlreadyControlled, NIL); { put up alert }
  655.                             Exit(StartConnection);
  656.                         END;
  657.                     OTHERWISE
  658.                         ;
  659.                 END;
  660.             END;
  661.  
  662.         IF (err <> noErr) THEN
  663.             Exit(StartConnection);
  664.  
  665.         { start the first PPCRead }
  666.         WITH gPPCGeneralRec.readParam DO
  667.             BEGIN
  668.                 ioCompletion := @ReadCompProc;
  669.                 sessRefNum := gPPCSessRefNum;
  670.                 bufferLength := sizeof(RemoteSCPBRec);
  671.                 bufferPtr := @gPPCReadBuffer;
  672.             END;
  673.         gPPCDataRead := FALSE;
  674.         err := PPCRead(@gPPCGeneralRec.readParam, TRUE);    { asynchronously }
  675.  
  676.         myWindow := GetNewWindow(kMyWindowResource, Ptr(NIL), WindowPtr(-1));
  677.         WindowName := GetResource('STR ', kMyWindowResource);
  678.         HLock(WindowName);
  679.         SetWTitle(myWindow, StringHandle(WindowName)^^);
  680.         HUnlock(WindowName);
  681.         gWindowButton := GetNewControl(kWindowButton, myWindow);
  682.         ShowWindow(myWindow);
  683.     END;
  684.  
  685. {------------------------------------------------------------------------------}
  686.  
  687. {$S Main}
  688.     PROCEDURE DoConnectToRemote;
  689.         VAR
  690.             err: OSErr;
  691.             thePPCEndPBRec: PPCEndPBRec;
  692.     BEGIN
  693.         IF (gPPCSessRefNum = 0) THEN
  694.             BEGIN    { Attempt to connect to remote }
  695.                 IF (myPPCBrowser(gPPCSessLocationName, gPortInfoRec) <> noErr) THEN
  696.                     Exit(DoConnectToRemote);
  697.                 StartConnection;
  698.             END
  699.         ELSE
  700.             BEGIN    { Disconect from Remote }
  701.                 WITH gPPCEndRec.endParam DO
  702.                     BEGIN
  703.                         ioCompletion := @EndCompProc;
  704.                         sessRefNum := gPPCSessRefNum;
  705.                     END;
  706.                 err := PPCEnd(@gPPCEndRec.endParam, TRUE);
  707.                 DisposeWindow(myWindow);
  708.                 myWindow := NIL;
  709.                 gWindowButton := NIL;
  710.                 gNoLocationZoneName := '';
  711.             END;
  712.     END;
  713.  
  714. {------------------------------------------------------------------------------}
  715.  
  716. {$S Main}
  717.     PROCEDURE StartConnectionFromFile (spec: FSSpec);
  718.         VAR
  719.             resRefNum: INTEGER;
  720.             portInfoRes: PortInfoHandle;
  721.             locationNameRes: LocationNameHandle;
  722.     BEGIN
  723.         resRefNum := FSpOpenResFile(spec, fsRdPerm);
  724.         IF (resRefNum = -1) THEN
  725.             Exit(StartConnectionFromFile);
  726.  
  727.         portInfoRes := PortInfoHandle(Get1Resource(kSavedPortInfoRec, kSavedConnectionID));
  728.         IF (portInfoRes <> NIL) THEN
  729.             BEGIN
  730.                 HLock(Handle(portInfoRes));
  731.                 DetachResource(Handle(portInfoRes));
  732.             END;
  733.  
  734.         locationNameRes := LocationNameHandle(Get1Resource(kSavedSessLocationName, kSavedConnectionID));
  735.         IF (locationNameRes <> NIL) THEN
  736.             BEGIN
  737.                 HLock(Handle(locationNameRes));
  738.                 DetachResource(Handle(locationNameRes));
  739.             END;
  740.         CloseResFile(resRefNum);
  741.  
  742.         IF (portInfoRes <> NIL) AND (locationNameRes <> NIL) THEN
  743.             BEGIN
  744.                 gPortInfoRec := portInfoRes^^;
  745.                 gPPCSessLocationName := locationNameRes^^;
  746.                 StartConnection;
  747.             END;
  748.  
  749.         IF (portInfoRes <> NIL) THEN
  750.             BEGIN
  751.                 HUnlock(Handle(portInfoRes));
  752.                 DisposeHandle(Handle(portInfoRes));
  753.             END;
  754.  
  755.         IF (locationNameRes <> NIL) THEN
  756.             BEGIN
  757.                 HUnlock(Handle(locationNameRes));
  758.                 DisposeHandle(Handle(locationNameRes));
  759.             END;
  760.     END;
  761.  
  762. {------------------------------------------------------------------------------}
  763.  
  764. {$S Main}
  765.     FUNCTION GetFileFilter (pb: CInfoPBPtr): BOOLEAN;
  766.     BEGIN
  767.         IF ((pb^.ioFlFndrInfo.fdType = kRemoteConnectionType) AND (pb^.ioFlFndrInfo.fdCreator = kRemoteCreator)) THEN
  768.             BEGIN
  769.                 GetFileFilter := FALSE
  770.             END
  771.         ELSE
  772.             BEGIN
  773.                 GetFileFilter := TRUE;
  774.             END;
  775.     END;
  776.  
  777. {$S Main}
  778.     PROCEDURE GetConnection;
  779.         VAR
  780.             fileFilter: FileFilterUPP;
  781.             reply: StandardFileReply;
  782.             typeList: SFTypeList;
  783.     BEGIN
  784.         { get gPortInfoRec and gPPCSessLocationName }
  785.         fileFilter := NIL;
  786.         typeList[0] := kRemoteConnectionType;
  787.         fileFilter := NewFileFilterProc(@GetFileFilter);
  788.         StandardGetFile(fileFilter, -1, NIL, reply);
  789.         IF (NOT reply.sfGood) THEN
  790.             Exit(GetConnection);
  791.  
  792.         StartConnectionFromFile(reply.sfFile);
  793.     END;
  794.  
  795. {------------------------------------------------------------------------------}
  796.  
  797. {$S Main}
  798.     PROCEDURE SaveConnection;
  799.         VAR
  800.             strH: StringHandle;
  801.             prompt: Str255;
  802.             defaultName: Str255;
  803.             reply: StandardFileReply;
  804.             result: OSErr;
  805.             resRefNum: INTEGER;
  806.             portInfoRes: PortInfoHandle;
  807.             locationNameRes: LocationNameHandle;
  808.     BEGIN
  809.         { save gPortInfoRec and gPPCSessLocationName }
  810.         strH := GetString(kSaveConnectionPrompt);
  811.         IF (strH = NIL) THEN
  812.             prompt := 'Save current connection as:'
  813.         ELSE
  814.             BEGIN
  815.                 prompt := strH^^;
  816.             END;
  817.  
  818.         IF gPPCSessLocationName.locationKindSelector = ppcNoLocation THEN
  819.             BEGIN
  820.                 strH := GetString(-16413);
  821.                 defaultName := strH^^;
  822.             END
  823.         ELSE
  824.             BEGIN
  825.                 defaultName := gPPCSessLocationName.nbpEntity.objStr;
  826.             END;
  827.  
  828.         StandardPutFile(prompt, defaultName, reply);
  829.         IF (NOT reply.sfGood) THEN
  830.             Exit(SaveConnection);
  831.  
  832.         IF (reply.sfReplacing) THEN
  833.             BEGIN
  834.                 IF (FSpDelete(reply.sfFile) <> noErr) THEN
  835.                     Exit(SaveConnection);
  836.             END;
  837.  
  838.         FSpCreateResFile(reply.sfFile, kRemoteCreator, kRemoteConnectionType, 0);
  839.         result := ResError;
  840.         IF (result = noErr) THEN
  841.             BEGIN
  842.                 resRefNum := FSpOpenResFile(reply.sfFile, fsRdWrPerm);
  843.                 IF (resRefNum = -1) THEN
  844.                     Exit(SaveConnection);
  845.  
  846.                 portInfoRes := PortInfoHandle(NewHandle(sizeof(PortInfoRec)));
  847.                 locationNameRes := LocationNameHandle(NewHandle(sizeof(LocationNameRec)));
  848.                 IF ((portInfoRes <> NIL) AND (locationNameRes <> NIL)) THEN
  849.                     BEGIN
  850.                         BlockMove(@gPortInfoRec, portInfoRes^, sizeof(PortInfoRec));
  851.                         AddResource(Handle(portInfoRes), kSavedPortInfoRec, kSavedConnectionID, '');
  852.                         result := ResError;
  853.                         IF (result = noErr) THEN
  854.                             BEGIN
  855.                                 BlockMove(@gPPCSessLocationName, locationNameRes^, sizeof(LocationNameRec));
  856.                                 AddResource(Handle(locationNameRes), kSavedSessLocationName, kSavedConnectionID, '');
  857.                                 result := ResError;
  858.                             END;
  859.                     END
  860.                 ELSE
  861.                     BEGIN
  862.                         result := memFullErr;
  863.                     END;
  864.  
  865.                 CloseResFile(resRefNum);
  866.  
  867.                 IF (result <> noErr) THEN
  868.                     BEGIN
  869.                         result := FSpDelete(reply.sfFile)
  870.                     END;
  871.             END;
  872.     END;
  873.  
  874. {------------------------------------------------------------------------------}
  875.  
  876. {$S Main}
  877. { my menu action taker }
  878.     PROCEDURE DoSelected (val: LongInt);
  879.         VAR
  880.             hival, loval: Integer;
  881.             qq: Integer;
  882.             DAname: Str255;
  883.     BEGIN
  884.         loval := LoWord(val);
  885.         hival := HiWord(val);
  886.  
  887.         CASE hival OF    { switch off the menu number selected }
  888.             kAppleMenu: 
  889.                 BEGIN
  890.                     IF (loval <> 1) THEN    {if this was not About, it's a DA }
  891.                         BEGIN
  892.                             GetMenuItemText(gAppleMenuHandle, loval, DAname);
  893.                             qq := OpenDeskAcc(DAname);
  894.                         END
  895.                     ELSE
  896.                         BEGIN
  897.                             ParamText(gShortVersStr, '', '', '');
  898.                             qq := Alert(kAboutBox, NIL);    { do about box }
  899.                         END;
  900.                 END;
  901.             kFileMenu: 
  902.                 BEGIN
  903.                     CASE loval OF
  904.                         kOpenItem: 
  905.                             BEGIN
  906.                                 GetConnection;
  907.                             END;
  908.                         kSaveItem: 
  909.                             BEGIN
  910.                                 SaveConnection;
  911.                             END;
  912.                         kQuitItem: 
  913.                             BEGIN
  914.                                 gQuit := TRUE;
  915.                             END;
  916.                         OTHERWISE
  917.                             ;
  918.                     END; { case loval }
  919.                 END;
  920.             kEditMenu: 
  921.                 BEGIN
  922.                { edit menu junk }
  923.             { don't care }
  924.                 END;
  925.             kRemoteMenu: 
  926.                 BEGIN
  927.                     IF (loval = kConnectToRemote) THEN
  928.                         DoConnectToRemote;
  929.                 END;
  930.             kHMHelpMenuID:    { Defined in Balloons }
  931.             { I only care about this item.  If anything else is returned here, I don't know what }
  932.             { it is, so I leave it alone.  Remember, the Help Manager chapter says that }
  933.             { Apple reserves the right to add and change things in the Help menu }
  934.                 BEGIN
  935.                     IF (loval = gHelpItem) THEN
  936.                         SampleHelpDialog;
  937.                 END;
  938.             OTHERWISE
  939.                 ;
  940.         END; { CASE hival }
  941.         HiliteMenu(0);
  942.     END;
  943.  
  944. {------------------------------------------------------------------------------}
  945.  
  946. {$S Main}
  947.     FUNCTION OpenPPCPort: OSErr;
  948.     { OpenPPCPort opens a PPC port for use by the server sessions.}
  949.     {  It initializes the port name and location name records.}
  950.     {  Then, it calls PPCOpen synchronously to open the port.  If the port was}
  951.     {  sucessfully opened, the gPPCPortOpen is set TRUE, and gPPCPortRefNum is set to}
  952.     {  the port reference number returned by PPCOpen.}
  953.     {  Any errors detected are passed back to PPCStartUp to be returned to}
  954.     {  the application. }
  955.  
  956.         VAR
  957.             thePortRec: PPCPortRec;                { the port name of the port to be opened. }
  958.             theOpenPBRec: PPCOpenPBRec;            { used by the PPCOpen call. }
  959.             err: OSErr;    { used to keep track of errors within the function. }
  960.     BEGIN
  961.         { initialize the port name record }
  962.         WITH thePortRec DO
  963.             BEGIN
  964.                 nameScript := GetScriptManagerVariable(smSysScript);    { use Script Manager call to get System Script }
  965.                 name := kPortName;                { This is the name that will show up in the }
  966.                                                 { "Programs" list that the Browser puts up.}
  967.                                                 { It should be a resource string instead of }
  968.                                                 { hard coded (as done here).}
  969.                 portKindSelector := ppcByCreatorAndType;    { port kind by creator/type }
  970.                 portCreator := kRemoteCreator;
  971.                 portType := OSType(kVerNumType);
  972.             END;
  973.  
  974.         { Now, set up Open parameter block record }
  975.         WITH theOpenPBRec DO
  976.             BEGIN
  977.                 ioCompletion := NIL;                { no completion Proc needed (synchronous) }
  978.                 serviceType := CHAR(ppcServiceRealTime);    { 7.0 only supports this type of service }
  979.                 resFlag := 0;                        { must be zero }
  980.                 portName := @thePortRec;            { pointer to port record}
  981.                 locationName := NIL;
  982.                 networkVisible := FALSE;             { No reason to be seen }
  983.             END;
  984.  
  985.         { execute PPCOpen synchronously and return any errors to caller }
  986.         err := PPCOpen(@theOpenPBRec, FALSE);
  987.         IF (err = noErr) THEN
  988.             BEGIN
  989.                 gPPCPortOpen := TRUE;                { set the global port open flag }
  990.                 gPPCPortRefNum := theOpenPBRec.portRefNum;    { set the global port reference number }
  991.             END;
  992.         OpenPPCPort := err;
  993.     END;
  994.  
  995. {------------------------------------------------------------------------------}
  996.  
  997. {$S Main}
  998.     PROCEDURE PPCShutDown;
  999.     { PPCShutDown first closes the PPC port that was opened by PPCStartUp.}
  1000.     {  Closing the port will automatically kill all sessions that use that port.}
  1001.     {  After closing the port, PPCShutDown disposes of all session records. }
  1002.  
  1003.         VAR
  1004.             theClosePBRec: PPCClosePBRec;
  1005.             err: OSErr;
  1006.     BEGIN
  1007.         { Close the port.  This will cause all PPC calls associated with this port }
  1008.         { to complete. }
  1009.         IF (gPPCPortOpen) THEN    { close the port }
  1010.             BEGIN
  1011.                 gPPCPortOpen := FALSE;    { tell completion routines we're shutting down }
  1012.                                         { so they won't try to restart a session }
  1013.                 theClosePBRec.ioCompletion := NIL;
  1014.                 theClosePBRec.portRefNum := gPPCPortRefNum;
  1015.                 err := PPCClose(@theClosePBRec, FALSE);
  1016.             END;
  1017.     END;
  1018.  
  1019. {------------------------------------------------------------------------------}
  1020.  
  1021. {$S Initialize}
  1022.     FUNCTION InitPPCStuff: Boolean;
  1023.         VAR
  1024.             PPCAttributes: LongInt;    {Storage for the response from Gestalt}
  1025.             err: OSErr;    {Temporary variable to catch errors}
  1026.     BEGIN
  1027.         InitPPCStuff := FALSE;
  1028.         IF (Gestalt(gestaltPPCToolboxAttr, PPCAttributes) <> noErr) THEN
  1029.             BEGIN
  1030.                 NotifyAndExit(kExitNoPPC); { Bail out now }
  1031.                 Exit(InitPPCStuff);
  1032.             END;
  1033.  
  1034.         { ELSE PPC Toolbox is present }
  1035.  
  1036.         { Does PPC need initialization? }
  1037.         IF (BAND(PPCAttributes, gestaltPPCSupportsRealTime) = 0) THEN
  1038.             BEGIN    { PPC Toolbox needs initialization }
  1039.                 { initialize the PPC Toolbox and set function result }
  1040.                 IF PPCInit = noErr THEN
  1041.                     { get the post-init attributes for the PPC Toolbox }
  1042.                     err := Gestalt(gestaltPPCToolboxAttr, PPCAttributes)
  1043.                 ELSE    { PPC can't be inited }
  1044.                     BEGIN
  1045.                         NotifyAndExit(kExitPPCInitFailed); { Bail out now }
  1046.                         Exit(InitPPCStuff);
  1047.                     END;
  1048.             END;
  1049.  
  1050.         { Make sure ports can be opened to the outside world }
  1051.         IF (BAND(PPCAttributes, gestaltPPCSupportsOutGoing) = 0) THEN
  1052.             { It's likely that AppleTalk is disabled, so you    }
  1053.             { may want to tell the user to activate AppleTalk    }
  1054.             { from the Chooser.    }
  1055.             BEGIN
  1056.                 NotifyAndExit(kExitAppleTalkDisabled); { Bail out now }
  1057.                 Exit(InitPPCStuff);
  1058.             END;
  1059.  
  1060.         IF (OpenPPCPort <> noErr) THEN
  1061.             { couldn't open a PPC port }
  1062.             BEGIN
  1063.                 NotifyAndExit(kExitPPCOpenFailed); { Bail out now }
  1064.                 Exit(InitPPCStuff);
  1065.             END;
  1066.  
  1067.         InitPPCStuff := TRUE;
  1068.     END;
  1069.  
  1070. {------------------------------------------------------------------------------}
  1071.  
  1072. {$S Main}
  1073. { This is the standard Open Application event. }
  1074.     FUNCTION AEOpenHandler (messagein: AppleEvent;
  1075.                                     reply: AppleEvent;
  1076.                                     refIn: LongInt): OSErr;
  1077.     BEGIN
  1078.     { of course, we don't do anything here in this simple app }
  1079.         AEOpenHandler := noErr;
  1080.     END;
  1081.  
  1082. {------------------------------------------------------------------------------}
  1083.  
  1084. {$S Main}
  1085. { Open Doc, opens our documents.  Remember, this can happen at application start AND }
  1086. { anytime else.  If your app is up and running and the user goes to the desktop, hilites one }
  1087. { of your files, and double-clicks or selects Open from the Finder File menu this event }
  1088. { handler will get called. Which means you don't do any initialization of globals here, or }
  1089. { anything else except open then doc.  }
  1090. { SO-- Do NOT assume that you are at app start time in this }
  1091. { routine, or bad things will surely happen to you. }
  1092.     FUNCTION AEOpenDocHandler (messagein: AppleEvent;
  1093.                                     reply: AppleEvent;
  1094.                                     refIn: LongInt): OSErr;
  1095.         VAR
  1096.             myFSS: FSSpec;
  1097.             docList: AEDescList;
  1098.             myErr: OSErr;
  1099.             itemsInList: LongInt;
  1100.             actualSize: Size;
  1101.             keywd: AEKeyword;
  1102.             returnedType: DescType;
  1103.     BEGIN
  1104.         IF (gPPCSessRefNum = 0) THEN
  1105.             BEGIN
  1106.             {get the direct parameter--a descriptor list--and put it into docList}
  1107.                 myErr := AEGetParamDesc(messagein, keyDirectObject, typeAEList, docList);
  1108.                 IF myErr <> noErr THEN
  1109.                     BEGIN
  1110.                         AEOpenDocHandler := myErr;
  1111.                         Exit(AEOpenDocHandler);
  1112.                     END;
  1113.  
  1114.             {count the number of descriptor records in the list}
  1115.                 myErr := AECountItems(docList, itemsInList);
  1116.                 IF myErr <> noErr THEN
  1117.                     BEGIN
  1118.                         AEOpenDocHandler := myErr;
  1119.                         Exit(AEOpenDocHandler);
  1120.                     END;
  1121.  
  1122.                 IF itemsInList > 0 THEN
  1123.                     BEGIN
  1124.                     { now get the first descriptor record from the list, coerce the returned }
  1125.                     { data to an FSSpec record, and start the connection }
  1126.                         myErr := AEGetNthPtr(docList, 1, typeFSS, keywd, returnedType, @myFSS, Sizeof(myFSS), actualSize);
  1127.                         StartConnectionFromFile(myFSS);
  1128.                     END;
  1129.                 myErr := AEDisposeDesc(docList);
  1130.             END
  1131.         ELSE
  1132.             BEGIN
  1133.                 AEOpenDocHandler := errAEEventNotHandled; { can't handle it now, a session is open }
  1134.             END;
  1135.         AEOpenDocHandler := myErr;
  1136.     END;
  1137.  
  1138. {------------------------------------------------------------------------------}
  1139.  
  1140. {$S Main}
  1141.     FUNCTION AEPrintHandler (messagein: AppleEvent;
  1142.                                     reply: AppleEvent;
  1143.                                     refIn: LongInt): OSErr;
  1144.     BEGIN
  1145.     { no printing handler in yet, so we'll ignore this }
  1146.     { the operation is functionally identical to the ODOC event, with the addition }
  1147.     { of calling your print routine.  }
  1148.     { we of course don't do anything here }
  1149.         AEPrintHandler := errAEEventNotHandled;    { we have no docs, so no pdoc events should come to us }
  1150.     END;
  1151.  
  1152. {------------------------------------------------------------------------------}
  1153.  
  1154. {$S Main}
  1155. { Standard Quit event handler, to handle a Quit event from the Finder, for example.    }
  1156. { ••••• DO NOT CALL EXITTOSHELL HERE ••••• or you will never have a happy life.    }
  1157.     FUNCTION AEQuitHandler (messagein: AppleEvent;
  1158.                                     reply: AppleEvent;
  1159.                                     refIn: LongInt): OSErr;
  1160.     BEGIN
  1161.     { prepQuit sets the Stop flag for us.  It does _NOT_ quit, you }
  1162.     { should NEVER quit from an AppleEvent handler.  Calling }
  1163.     { ExitToShell here would blow things up }
  1164.         gQuit := TRUE;
  1165.         AEQuitHandler := noErr;
  1166.     END;
  1167.  
  1168. {------------------------------------------------------------------------------}
  1169.  
  1170. {$S Main}
  1171. { I'm not doing error handling in this sample for clarities sake, you should. }
  1172. { Hah, easy for me to say, huh? }
  1173.     PROCEDURE DoHighLevel (AERecord: EventRecord);
  1174.         VAR
  1175.             err: OSErr;
  1176.     BEGIN
  1177.         err := AEProcessAppleEvent(AERecord);
  1178.     END;
  1179.  
  1180. {------------------------------------------------------------------------------}
  1181.  
  1182. {$S Main}
  1183.     PROCEDURE FixFileMenu;
  1184.     BEGIN
  1185.         IF (gPPCSessRefNum = 0) THEN
  1186.             { Enable Open and disable Save }
  1187.             BEGIN
  1188.                 EnableItem(gFileMenuHandle, kOpenItem);
  1189.                 DisableItem(gFileMenuHandle, kSaveItem);
  1190.             END
  1191.         ELSE
  1192.             { Disable Open and enable Save }
  1193.             BEGIN
  1194.                 DisableItem(gFileMenuHandle, kOpenItem);
  1195.                 EnableItem(gFileMenuHandle, kSaveItem);
  1196.             END;
  1197.     END;
  1198.  
  1199. {------------------------------------------------------------------------------}
  1200.  
  1201. {$S Main}
  1202.     PROCEDURE FixSharingMenu;
  1203.         VAR
  1204.             menuString: Str255;
  1205.     BEGIN
  1206.         IF (gPPCSessRefNum = 0) THEN
  1207.             GetIndString(menuString, kRemoteMenu, kConnectString)
  1208.         ELSE
  1209.             GetIndString(menuString, kRemoteMenu, kDisconnectString);
  1210.  
  1211.         SetMenuItemText(gRemoteMenuHandle, kConnectToRemote, menuString);
  1212.     END;
  1213.  
  1214. {------------------------------------------------------------------------------}
  1215.  
  1216. {$S Main}
  1217.     PROCEDURE PPCProcessReads;
  1218.         VAR
  1219.             h: Handle;
  1220.             SCpbPtr: RemoteSCPBRecPtr;
  1221.             err: OSErr;
  1222.             bttnStr: Str255;
  1223.     BEGIN
  1224.         IF (gPPCDataRead) THEN    { process the data read }
  1225.             BEGIN
  1226.                 SCpbPtr := RemoteSCPBRecPtr(@gPPCReadBuffer); { type-cast data read }
  1227.                 IF ((gFirstStatus) OR (SCpbPtr^.scPB.pollServerPB.scServerState <> oldSCServerState)) THEN
  1228.                     BEGIN
  1229.                         { store current server state so we don't have to update needlessly }
  1230.                         gFirstStatus := FALSE;
  1231.                         oldSCServerState := SCpbPtr^.scPB.pollServerPB.scServerState;
  1232.  
  1233.                         SetPort(myWindow);
  1234.                         h := GetResource('RECT', kRemoteStatusText);
  1235.                         HLock(h);
  1236.                         InvalRect(RectHndl(h)^^);
  1237.                         HUnlock(h);
  1238.  
  1239.                         CASE oldSCServerState OF
  1240.                             SCPSJustDisabled, SCPSSleeping: 
  1241.                                 BEGIN
  1242.                                     GetIndString(bttnStr, kWindowButton, kButtonStart);
  1243.                                     SetControlTitle(gWindowButton, bttnStr);
  1244.                                 END;
  1245.                             SCPSRunning: 
  1246.                                 BEGIN
  1247.                                     GetIndString(bttnStr, kWindowButton, kButtonStop);
  1248.                                     SetControlTitle(gWindowButton, bttnStr);
  1249.                                 END;
  1250.                             OTHERWISE { SCPSStartingUp, or shutting down (0..4094) }
  1251.                                 BEGIN
  1252.                                     GetIndString(bttnStr, kWindowButton, kButtonCancel);
  1253.                                     SetControlTitle(gWindowButton, bttnStr);
  1254.                                 END;
  1255.                         END;
  1256.                     END;
  1257.  
  1258.                 { Now, fill out the readParam parameter block and call PPCRead }
  1259.                 WITH gPPCGeneralRec.readParam DO
  1260.                     BEGIN
  1261.                         ioCompletion := @ReadCompProc;
  1262.                         bufferLength := sizeof(RemoteSCPBRec); { full buffer size again }
  1263.                         { We're reusing the same parameter block, so the sessRefNum }
  1264.                         { is already filled in for us. }
  1265.                         bufferPtr := @gPPCReadBuffer;
  1266.                     END;
  1267.                 gPPCDataRead := FALSE;
  1268.                 err := PPCRead(@gPPCGeneralRec.readParam, TRUE);     { asynchronously }
  1269.             END;
  1270.     END;
  1271.  
  1272. {------------------------------------------------------------------------------}
  1273.  
  1274. {$S Main}
  1275.     PROCEDURE doNullEvt;
  1276.     BEGIN
  1277.         IF (gPPCSessRefNum <> 0) THEN
  1278.             BEGIN
  1279.                 PPCProcessReads;
  1280.             END
  1281.         ELSE IF (myWindow <> NIL) THEN
  1282.             { The session is gone, but the window is open. Close it! }
  1283.             BEGIN
  1284.                 DisposeWindow(myWindow);
  1285.                 myWindow := NIL;
  1286.                 gWindowButton := NIL;
  1287.                 gNoLocationZoneName := '';
  1288.             END;
  1289.     END;
  1290.  
  1291. {------------------------------------------------------------------------------}
  1292.  
  1293. {$S Main}
  1294.     PROCEDURE WriteCompProc (pb: PPCParamBlockPtr);
  1295.     { This procedure gets called when the asynchronous PPCWrite call completes.}
  1296.     { If no errors are detected, then it puts the parameter block in the }
  1297.     { gWpbQueue where the PollTheServer procedure will find it next time it }
  1298.     { needs to send data to the remote. }
  1299.     { If an error is detected, then PPCEnd is called asynchronously to close}
  1300.     { the session. }
  1301.  
  1302.         VAR
  1303.             err: OSErr;    { used to catch the PPC function results. }
  1304.  
  1305.     BEGIN
  1306.         IF (PPCWritePBPtr(pb)^.ioResult = noErr) THEN
  1307.             BEGIN
  1308.                 gPPCWriteInProgress := FALSE;
  1309.                 err := WakeUpProcess(gOurPSN); { update button control ASAP }
  1310.             END
  1311.         ELSE
  1312.             BEGIN
  1313.                 { if we get an error, then we call PPCEnd to close up cleanly}
  1314.                 PPCEndPBPtr(pb)^.ioCompletion := @EndCompProc;
  1315.                 err := PPCEndAsync(PPCEndPBPtr(pb));
  1316.             END;
  1317.     END;
  1318.  
  1319. {------------------------------------------------------------------------------}
  1320.  
  1321. {$S Main}
  1322.     PROCEDURE DrawOKoutline (theDialog: DialogPtr;
  1323.                                     itemNo: Integer);
  1324. { draw the dialogLine and okOutline user items }
  1325.         VAR
  1326.             itemType: Integer;
  1327.             item: Handle;
  1328.             box: Rect;
  1329.     BEGIN
  1330.         GetDialogItem(theDialog, ok, itemType, item, box);
  1331.         InsetRect(box, -4, -4);
  1332.         PenSize(3, 3);
  1333.         FrameRoundRect(box, 16, 16);
  1334.         PenSize(1, 1);
  1335.     END;
  1336.  
  1337. {------------------------------------------------------------------------------}
  1338.  
  1339. {$S Main}
  1340.     PROCEDURE IBeamIt (theDialog: DialogPtr);
  1341.         VAR
  1342.             itemNum: Integer;
  1343.             kind: Integer;
  1344.             h: Handle;
  1345.             r: Rect;
  1346.             pt: Point;
  1347.     BEGIN
  1348.         itemNum := DialogPeek(theDialog)^.EditField + 1;
  1349.         GetDialogItem(theDialog, itemNum, kind, h, r);
  1350.         GetMouse(pt);
  1351.         IF (PtInRect(pt, r)) THEN
  1352.             SetCursor(GetCursor(1)^^)
  1353.         ELSE
  1354.             InitCursor;
  1355.     END;
  1356.  
  1357. {------------------------------------------------------------------------------}
  1358.  
  1359. {$S Main}
  1360.  
  1361.     FUNCTION SnatchHandle (theDialog: DialogPtr;
  1362.                                     item: Integer): ControlHandle;
  1363.         VAR
  1364.             kind: Integer;
  1365.             h: Handle;
  1366.             r: Rect;
  1367.     BEGIN
  1368.         GetDialogItem(theDialog, item, kind, h, r);
  1369.         SnatchHandle := ControlHandle(h);
  1370.     END;
  1371.  
  1372. {------------------------------------------------------------------------------}
  1373.  
  1374. {$S Main}
  1375.     FUNCTION RangeIsSelected (theDialog: DialogPtr): Boolean;
  1376. { See if theDialog's current edit item has any text selected }
  1377.     BEGIN
  1378.         WITH TEHandle(DialogPeek(theDialog)^.textH)^^ DO
  1379.             RangeIsSelected := (selStart <> selEnd);
  1380.     END;
  1381.  
  1382. {------------------------------------------------------------------------------}
  1383.  
  1384. {$S Main}
  1385.     FUNCTION ShutdownDialogFilter (theDialog: DialogPtr;
  1386.                                     VAR theEvent: EventRecord;
  1387.                                     VAR itemHit: Integer): Boolean;
  1388.         CONST
  1389.             enterChar = $03;
  1390.             deleteChar = $08;
  1391.             returnChar = $0D;
  1392.             escapeChar = $1B;
  1393.             leftArrowChar = $1C;
  1394.             rightArrowChar = $1D;
  1395.             upArrowChar = $1E;
  1396.             downArrowChar = $1F;
  1397.         VAR
  1398.             temp: WindowPtr;
  1399.             chCode: Integer;
  1400.             finalTicks: LongInt;
  1401.             itemNum: Integer;
  1402.             kind: Integer;
  1403.             h: Handle;
  1404.             r: Rect;
  1405.             pt: Point;
  1406.             myStr: Str255;
  1407.     BEGIN
  1408.         GetPort(temp);
  1409.         SetPort(theDialog);
  1410.         ShutdownDialogFilter := FALSE;
  1411.         IBeamIt(theDialog);
  1412.         WITH theEvent DO
  1413.             IF ((what = keyDown) OR (what = autoKey)) THEN
  1414.                 BEGIN
  1415.                     chCode := BitAnd(message, CharCodeMask);
  1416.                     CASE chCode OF
  1417.                         enterChar, returnChar: 
  1418.                             BEGIN
  1419.                                 itemHit := ok;
  1420.                                 HiliteControl(SnatchHandle(theDialog, itemHit), kControlButtonPart);
  1421.                                 Delay(8, finalTicks);
  1422.                                 HiliteControl(SnatchHandle(theDialog, itemHit), kControlNoPart);
  1423.                                 SetPort(temp);
  1424.                                 ShutdownDialogFilter := TRUE;
  1425.                             END;
  1426.                         escapeChar: 
  1427.                             BEGIN
  1428.                                 itemHit := cancel;
  1429.                                 HiliteControl(SnatchHandle(theDialog, itemHit), kControlButtonPart);
  1430.                                 Delay(8, finalTicks);
  1431.                                 HiliteControl(SnatchHandle(theDialog, itemHit), kControlNoPart);
  1432.                                 SetPort(temp);
  1433.                                 ShutdownDialogFilter := TRUE;
  1434.                             END;
  1435.                         OTHERWISE
  1436.                             IF ((DialogPeek(theDialog)^.editField + 1) = kShutdownTime) THEN
  1437.                                 BEGIN
  1438.                                     GetDialogItem(theDialog, kShutdownTime, kind, h, r);
  1439.                                     GetDialogItemText(h, myStr);
  1440.                                     IF (NOT (chCode IN [deleteChar, leftArrowChar..downArrowChar])) THEN
  1441.                                         IF (((NOT RangeIsSelected(theDialog)) AND (Length(myStr) >= 3)) OR (NOT (chCode IN [$30..$39]))) THEN
  1442.                                             BEGIN
  1443.                                                 SysBeep(1);
  1444.                                                 ShutdownDialogFilter := TRUE;
  1445.                                             END;
  1446.                                 END;
  1447.                     END; { case }
  1448.                 END;
  1449.     END;
  1450.  
  1451. {------------------------------------------------------------------------------}
  1452.  
  1453. {$S Main}
  1454.     FUNCTION doGetShutDownTime (VAR minutes: Integer): Boolean;
  1455.         VAR
  1456.             dPtr: DialogPtr;
  1457.             itemHit: Integer;
  1458.  
  1459.             itemType: Integer;
  1460.             item: Handle;
  1461.             box: Rect;
  1462.  
  1463.             ShutdownTimeStr: Str255;
  1464.             ShutdownTime: LongInt;
  1465.     BEGIN
  1466.         dPtr := GetNewDialog(kShutdownDialog, NIL, pointer(-1));
  1467.  
  1468.         { set procedure pointer for OK button outline }
  1469.         GetDialogItem(dPtr, kShutdownOKOutline, itemType, item, box);
  1470.         SetDialogItem(dPtr, kShutdownOKOutline, itemType, Handle(@DrawOKoutline), box);
  1471.  
  1472.         ShowWindow(dPtr);
  1473.  
  1474.         SelectDialogItemText(dPtr, kShutdownTime, 0, 32767);
  1475.         REPEAT
  1476.             ModalDialog(@ShutdownDialogFilter, itemHit);
  1477.         UNTIL ((itemHit = OK) OR (itemHit = cancel));
  1478.         IF (itemHit = OK) THEN
  1479.             BEGIN
  1480.                 doGetShutDownTime := TRUE;
  1481.                 { Grab the minutes }
  1482.                 GetDialogItem(dPtr, kShutdownTime, itemType, item, box);
  1483.                 GetDialogItemText(item, ShutdownTimeStr);
  1484.                 StringToNum(ShutdownTimeStr, ShutdownTime);
  1485.                 minutes := ShutdownTime;
  1486.             END
  1487.         ELSE
  1488.             doGetShutDownTime := FALSE;
  1489.         DisposeDialog(dPtr);
  1490.     END;
  1491.  
  1492. {------------------------------------------------------------------------------}
  1493.  
  1494. {$S Main}
  1495.     PROCEDURE doMyWindContent (window: WindowPtr;
  1496.                                     event: EventRecord);
  1497.         VAR
  1498.             part: Integer;
  1499.             control: ControlHandle;
  1500.             err: OSErr;
  1501.             sendIt: Boolean;
  1502.     BEGIN
  1503.         SetPort(window);
  1504.         GlobalToLocal(event.where); {convert to local coordinates}
  1505.         IF (FindControl(event.where, window, control) <> 0) THEN
  1506.             IF (TrackControl(control, event.where, NIL) <> 0) THEN
  1507.                 BEGIN    { there's only one control to track and it was used }
  1508.                     IF (NOT gPPCWriteInProgress) THEN
  1509.                         BEGIN { send a server command }
  1510.                             CASE oldSCServerState OF
  1511.                                 SCPSJustDisabled:    { send SCStartServer }
  1512.                                     BEGIN
  1513.                                         WITH gRemoteSCpb.scPB.startPB DO
  1514.                                             BEGIN
  1515.                                                 scCode := SCStartServer;
  1516.                                                 scStartSelect := kCurInstalled;
  1517.                                                 scEventSelect := kFinderExtn;
  1518. {scWhere := LongInt('fext');}
  1519. {scReceiverID := LongInt('MACS');}
  1520. {scDataType := LongInt('fext');}
  1521. {scStartOptions := 0;}
  1522.                                             END;
  1523.                                         sendIt := TRUE;
  1524.                                     END;
  1525.                                 SCPSSleeping:    { send SCWakeServer }
  1526.                                     BEGIN
  1527.                                         gRemoteSCpb.scPB.startPB.scCode := SCWakeServer;
  1528.                                         sendIt := TRUE;
  1529.                                     END;
  1530.                                 SCPSRunning:        { send SCShutDown with x minutes }
  1531.                                     BEGIN
  1532.                                         IF (doGetShutDownTime(gRemoteSCpb.scPB.disconnectPB.scNumMinutes)) THEN
  1533.                                             BEGIN
  1534.                                                 WITH gRemoteSCpb.scPB.disconnectPB DO
  1535.                                                     BEGIN
  1536.                                                         scCode := SCShutDown;
  1537.                                                         scDiscArrayPtr := NIL;
  1538.                                                         scArrayCount := 0;
  1539.                                                         { scNumMinutes filled in already }
  1540.                                                         scFlags := 0; { we have no message }
  1541.                                                                       { $2000 if we did have message }
  1542.                                                         scMessagePtr := NIL; { set by server controller }
  1543.                                                         gRemoteSCpb.scMessageOrName := '';
  1544.                                                     END;
  1545.                                                 sendIt := TRUE;
  1546.                                             END
  1547.                                         ELSE
  1548.                                             sendIt := FALSE; { canceled at doGetShutDownTime }
  1549.                                     END;
  1550.                                 SCPSStartingUp:    { send SCShutDown with 0 minutes }
  1551.                                     BEGIN
  1552.                                         WITH gRemoteSCpb.scPB.disconnectPB DO
  1553.                                             BEGIN
  1554.                                                 scCode := SCShutDown;
  1555.                                                 scDiscArrayPtr := NIL;
  1556.                                                 scArrayCount := 0;
  1557.                                                 scNumMinutes := 0;
  1558.                                                 scFlags := 0; { no message }
  1559.                                                 gRemoteSCpb.scMessageOrName := '';
  1560.                                                 scMessagePtr := NIL; { set by server controller }
  1561.                                             END;
  1562.                                         sendIt := TRUE;
  1563.                                     END;
  1564.                                 OTHERWISE { shutting down (0..4094), so send SCCancelShutDown }
  1565.                                     BEGIN
  1566.                                         gRemoteSCpb.scPB.disconnectPB.scCode := SCCancelShutDown;
  1567.                                         sendIt := TRUE;
  1568.                                     END;
  1569.                             END;
  1570.  
  1571.                             IF (sendIt) THEN { now, send it }
  1572.                                 BEGIN
  1573.                                     WITH gPPCWriteRec.writeParam DO
  1574.                                         BEGIN
  1575.                                             ioCompletion := @WriteCompProc;
  1576.                                             sessRefNum := gPPCSessRefNum;
  1577.                                             bufferLength := sizeof(RemoteSCPBRec);
  1578.                                             bufferPtr := @gRemoteSCpb;
  1579.                                             more := FALSE;
  1580.                                             { I'm not using userData, blockCreator, or blockType }
  1581.                                         END;
  1582.  
  1583.                                     gPPCWriteInProgress := TRUE;
  1584.                                     err := PPCWriteAsync(PPCWritePBPtr(@gPPCWriteRec));
  1585.                                 END;
  1586.                         END
  1587.                     ELSE
  1588.                         BEGIN { should never get to here - button is disabled during writes }
  1589.                             SysBeep(1);
  1590.                         END;
  1591.                 END; { IF TrackControl... }
  1592.     END;
  1593.  
  1594. {------------------------------------------------------------------------------}
  1595.  
  1596. {$S Main}
  1597.     PROCEDURE DoEventLoop;
  1598. { yeah, first I took it out of the main program and now I think I'll split it up }
  1599. { into smaller pieces.  However, it'll still be in the same source code file }
  1600. { (don't you hate having to look all over hell for code) }
  1601.         VAR
  1602.             twindow: WindowPtr;
  1603.             evtRecord: EventRecord;
  1604.             bob: Boolean;
  1605.     BEGIN
  1606.         REPEAT
  1607.             bob := WaitNextEvent(everyEvent, evtRecord, 30, NIL);
  1608.             HiliteMyButton;
  1609.             CASE evtRecord.what OF
  1610.                 nullEvent: 
  1611.                     ;
  1612.                 mouseDown:
  1613.                     { first, see where the hit was }
  1614.                     BEGIN
  1615.                         CASE FindWindow(Point(evtRecord.where), twindow) OF
  1616.                             inDesk:
  1617.                                 { if they hit in desk, then the process manager }
  1618.                                 { will switch us out, we don't need to do anything }
  1619.                                 BEGIN
  1620.                                 END;
  1621.                             inMenuBar: 
  1622.                                 BEGIN
  1623.                                     FixFileMenu;
  1624.                                     FixSharingMenu;
  1625.                                     DoSelected(MenuSelect(evtRecord.where));
  1626.                                 END;
  1627.                             inSysWindow:
  1628.                                 { pass to the system }
  1629.                                 BEGIN
  1630.                                     SystemClick(evtRecord, twindow);
  1631.                                 END;
  1632.                             inContent:
  1633.                                 { handle content and control clicks here }
  1634.                                 BEGIN
  1635.                                     IF (twindow <> FrontWindow) THEN
  1636.                                         SelectWindow(twindow)
  1637.                                     ELSE IF (twindow = myWindow) THEN
  1638.                                         BEGIN
  1639.                                             doMyWindContent(twindow, evtRecord);
  1640.                                         END;
  1641.                                 END;
  1642.                             inDrag: 
  1643.                                 BEGIN
  1644.                                     IF (twindow = FrontWindow) THEN
  1645.                                         DragWindow(twindow, Point(evtRecord.where), qd.screenBits.bounds);
  1646.                                 END;
  1647.                             inGrow:
  1648.                                 { Call GrowWindow here if you have a grow box }
  1649.                                 BEGIN
  1650.                                 END;
  1651.                             inGoAway:
  1652.                                 { Click in Close box }
  1653.                                 BEGIN
  1654.                                 END;
  1655.                             OTHERWISE
  1656.                                 ;
  1657.                         END;    { CASE FindWindow(evtRecord.message, twindow) }
  1658.                     END;
  1659.                 mouseUp: 
  1660.                     ;
  1661.                     { don't care }
  1662.                 keyDown, { same action for key or auto key }
  1663.                 autoKey: 
  1664.                     IF (BAND(evtRecord.modifiers, cmdKey) <> 0) THEN
  1665.                         BEGIN
  1666.                             FixFileMenu;
  1667.                             FixSharingMenu;
  1668.                             DoSelected(MenuKey(CHAR(BAND(evtRecord.message, charCodeMask))));
  1669.                         END;
  1670.                 keyUp: 
  1671.                     ;
  1672.                     { don't care }
  1673.                 updateEvt:
  1674.                     { draw whatever window needs an update }
  1675.                     DrawMain(WindowPtr(evtRecord.message));
  1676.                 diskEvt:
  1677.                     { I don't do anything special for disk events, this just passes them }
  1678.                     { to a function that checks for an error on the mount }
  1679.                     DoDiskEvents(evtRecord.message);
  1680.                 activateEvt: 
  1681.                     IF (BAND(evtRecord.modifiers, activeFlag) <> 0) THEN
  1682.                         DrawMain(WindowPtr(evtRecord.message));
  1683.                 osEvt: 
  1684.                     CASE BSR(evtRecord.message, 24) OF    { high byte of message }
  1685.                         mouseMovedMessage: 
  1686.                             ;
  1687.                             { don't care }
  1688.                         suspendResumeMessage:    {suspend/resume is also an activate/deactivate }
  1689.                             BEGIN
  1690.                                 gInBackground := BAND(evtRecord.message, resumeFlag) = 0;
  1691.                                 IF NOT gInBackGround THEN
  1692.                                     InitCursor;
  1693.                             END;
  1694.                         OTHERWISE
  1695.                             ;
  1696.                     END;
  1697.                 kHighLevelEvent: 
  1698.                     DoHighLevel(evtRecord);
  1699.                 OTHERWISE
  1700.                     ;
  1701.             END; { CASE evtRecord.what }
  1702.             doNullEvt;
  1703.         UNTIL (gQuit = TRUE);
  1704.     END;    { DoEventLoop }
  1705.  
  1706. {------------------------------------------------------------------------------}
  1707.  
  1708. {$S Initialize}
  1709.     PROCEDURE DoSetupMenus;
  1710.         VAR
  1711.             helpHandle: MenuHandle;
  1712.             helpString: StringHandle;
  1713.             count: Integer;
  1714.     BEGIN
  1715.         gMymenu := GetNewMBar(kMBarID);
  1716.         SetMenuBar(gMymenu);
  1717.         gAppleMenuHandle := GetMenuHandle(kAppleMenu);
  1718.         gFileMenuHandle := GetMenuHandle(kFileMenu);
  1719.         gEditMenuHandle := GetMenuHandle(kEditMenu);
  1720.         gRemoteMenuHandle := GetMenuHandle(kRemoteMenu);
  1721.         AppendResMenu(gAppleMenuHandle, 'DRVR');
  1722.  
  1723.     { now install my Help menu item in the Help Manager's menu }
  1724.         IF (HMGetHelpMenuHandle(helpHandle) = noErr) THEN    { Get the Help menu handle }
  1725.             BEGIN
  1726.                 count := CountMItems(helpHandle);    { How many items are there? }
  1727.                 helpString := GetString(kHelpString);{ get my help menu item string }
  1728.                 DetachResource(Handle(helpString));    { detach it }
  1729.                 HNoPurge(Handle(helpString));
  1730.                 MoveHHi(Handle(helpString));
  1731.                 HLock(Handle(helpString));
  1732.                 InsertMenuItem(helpHandle, helpString^^, count + 1);    { insert my item in the Help menu }
  1733.                 gHelpItem := CountMItems(helpHandle);    { The number of the item }
  1734.             END
  1735.         ELSE
  1736.             gHelpItem := 0;    { error - set it to something that we'll never see }
  1737.  
  1738.         DrawMenuBar;
  1739.     END;
  1740.  
  1741. {------------------------------------------------------------------------------}
  1742.  
  1743. {$S Initialize}
  1744.     FUNCTION InitAEStuff: Boolean;
  1745.         VAR
  1746.             err: OSErr;
  1747.     BEGIN
  1748.         { The following series of calls install our AppleEvent Handlers.    }
  1749.         { These handlers are added to the application event handler list    }
  1750.         { that the AppleEvent manager maintains.  So, whenever an            }
  1751.         { AppleEvent happens and we call AEProcessEvent, the AppleEvent        }
  1752.         { manager will check our list of handlers and dispatch to the        }
  1753.         { the correct handler if there is one.    }
  1754.         err := AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, @AEOpenHandler, 0, false);
  1755.         IF (err = noErr) THEN
  1756.             err := AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, @AEOpenDocHandler, 0, false);
  1757.         IF (err = noErr) THEN
  1758.             err := AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, @AEQuitHandler, 0, false);
  1759.         IF (err = noErr) THEN
  1760.             err := AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, @AEPrintHandler, 0, false);
  1761.  
  1762.         IF (err <> noErr) THEN
  1763.             NotifyAndExit(kExitAEHandlerNotInstalled); { Bail out now }
  1764.  
  1765.         InitAEStuff := (err = noErr);
  1766.     END;
  1767.  
  1768. {------------------------------------------------------------------------------}
  1769.  
  1770. {$S Initialize}
  1771.     FUNCTION NumToolboxTraps: Integer;
  1772.     BEGIN
  1773.         IF (NGetTrapAddress(_InitGraf, ToolTrap) = NGetTrapAddress($AA6E, ToolTrap)) THEN
  1774.             NumToolboxTraps := $200
  1775.         ELSE
  1776.             NumToolboxTraps := $400;
  1777.     END;
  1778.  
  1779. {------------------------------------------------------------------------------}
  1780.  
  1781. {$S Initialize}
  1782.     FUNCTION GetTrapType (theTrap: Integer): TrapType;
  1783.         CONST
  1784.             TrapMask = $0800;
  1785.     BEGIN
  1786.         IF (BAND(theTrap, TrapMask) > 0) THEN
  1787.             GetTrapType := ToolTrap
  1788.         ELSE
  1789.             GetTrapType := OSTrap;
  1790.     END;
  1791.  
  1792. {------------------------------------------------------------------------------}
  1793.  
  1794. {$S Initialize}
  1795.     FUNCTION TrapAvailable (theTrap: Integer): Boolean;
  1796.         VAR
  1797.             tType: TrapType;
  1798.     BEGIN
  1799.         tType := GetTrapType(theTrap);
  1800.         IF (tType = ToolTrap) THEN
  1801.             BEGIN
  1802.                 theTrap := BAND(theTrap, $07FF);
  1803.                 IF (theTrap >= NumToolboxTraps) THEN
  1804.                     theTrap := _Unimplemented;
  1805.             END;
  1806.         TrapAvailable := NGetTrapAddress(theTrap, tType) <> NGetTrapAddress(_Unimplemented, ToolTrap)
  1807.     END;
  1808.  
  1809. {------------------------------------------------------------------------------}
  1810.  
  1811. {$S Initialize}
  1812.     PROCEDURE InitializeApp;
  1813.         VAR
  1814.             vers: LongInt;
  1815.             err: OSErr;
  1816.             aLong: LongInt;
  1817.             macintoshName: StringHandle;
  1818.             savedResFile: Integer;
  1819.             i: Integer;
  1820.             curVersion: VersRecHndl;
  1821.     BEGIN
  1822.         myWindow := NIL;
  1823.         gWindowButton := NIL;
  1824.         gQuit := FALSE;
  1825.  
  1826.         gPPCPortOpen := FALSE;
  1827.         gPPCPortRefNum := 0;
  1828.         gFirstStatus := TRUE;    { reset gFirstStatus so first status returned by a }
  1829.                                 { new connection updates the status message }
  1830.         gPPCSessRefNum := 0;
  1831.         gPPCDataRead := FALSE;
  1832.         gPPCWriteInProgress := FALSE;
  1833.  
  1834.         err := GetCurrentProcess(gOurPSN);    { so completion routines can wake us up }
  1835.  
  1836.         MaxApplZone;
  1837.         { MoreMasters go here if you need'm }
  1838.         InitGraf(@qd.thePort);
  1839.         InitFonts;
  1840.         InitWindows;
  1841.         InitMenus;
  1842.         TEInit;
  1843.         InitDialogs(NIL);
  1844.         InitCursor;
  1845.  
  1846.         IF (NOT TrapAvailable(_Gestalt)) THEN
  1847.             BEGIN
  1848.                 { If Gestalt isn't available, then we can't even notify the user }
  1849.                 { because we can't see if the Notification Manager is available }
  1850.                 SysBeep(1);        { so ring the bell }
  1851.                 ExitToShell;    { and exit }
  1852.             END;
  1853.  
  1854.         { see if Notification Manager is available to display error messages }
  1855.         gNotificationMgrPresent := Gestalt(gestaltNotificationMgrAttr, aLong) = noErr;
  1856.  
  1857.         FOR i := 1 TO kNumExitErrors DO
  1858.             GetIndString(gNMStrs[i], kExitErrorStrings, i);
  1859.  
  1860.         { Check system version }
  1861.         vers := 0;
  1862.         err := Gestalt(gestaltSystemVersion, vers);
  1863.         IF (LoWord(vers) < $0700) THEN
  1864.             BEGIN
  1865.                 NotifyAndExit(kExitNoSystem7); { Bail out now }
  1866.                 Exit(InitializeApp);
  1867.             END;
  1868.  
  1869.         { Check this machine for AppleEvents. }
  1870.         { If they are not here, then we exit }
  1871.         IF (Gestalt(gestaltAppleEventsAttr, aLong) <> noErr) THEN
  1872.             BEGIN
  1873.                 NotifyAndExit(kExitNoAppleEvts); { Bail out now }
  1874.                 Exit(InitializeApp);
  1875.             END;
  1876.  
  1877.         IF (NOT InitAEStuff) THEN
  1878.             Exit(InitializeApp);
  1879.  
  1880.         curVersion := VersRecHndl(Get1Resource('vers', 1));
  1881.         IF (curVersion <> NIL) THEN
  1882.             BEGIN    { get version info }
  1883.                 gShortVersStr := curVersion^^.shortVersion;    { short version string }
  1884.             END
  1885.         ELSE
  1886.             BEGIN    { at least initialize them }
  1887.                 gShortVersStr := '';
  1888.             END;
  1889.  
  1890.         IF (NOT InitPPCStuff) THEN
  1891.             Exit(InitializeApp);
  1892.  
  1893.         DoSetupMenus;    { set up my menus }
  1894.  
  1895.         gNoLocationZoneName := '';
  1896.     END;
  1897.  
  1898. {------------------------------------------------------------------------------}
  1899.  
  1900. {PROCEDURE _DataInit;}
  1901. {External;}
  1902. { this is the application initialization code }
  1903.  
  1904. {------------------------------------------------------------------------------}
  1905.  
  1906. {$S Main}
  1907. BEGIN
  1908.     {UnloadSeg(@_DataInit);}
  1909.     { throw out the setup code }
  1910.     InitializeApp;
  1911.     UnloadSeg(@InitializeApp);    { get rid of my initialization code }
  1912.     DoEventLoop;
  1913.     PPCShutDown
  1914. END. { Main }